package http.secure.common.util.steganography;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * 字体隐写算法实现
 * 基于字体文件的字形数据进行信息隐藏
 */
public class BinarySteganography {
    private static final int HEADER_SIZE = 32; // 头部信息大小

    /**
     * 在二进制文件中隐藏信息
     *
     * @param binaryData 原始二进制数据
     * @param message    要隐藏的信息
     * @return 隐写后的二进制数据
     */
    public static byte[] hideMessage(byte[] binaryData, String message) {
        try {
            // 准备要隐藏的数据
            byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
            byte[] header = createHeader(messageBytes.length);
            byte[] dataToHide = combineArrays(header, messageBytes);
            // 检查容量
            if (!checkCapacity(binaryData, dataToHide)) {
                throw new IllegalStateException("字体文件容量不足，无法隐藏指定信息");
            }
            // 执行隐写
            return embedData(binaryData, dataToHide);
        } catch (Exception e) {
            throw new IllegalStateException("字体隐写失败：" + e.getMessage());
        }
    }

    /**
     * 创建头部信息
     */
    private static byte[] createHeader(int messageLength) {
        byte[] header = new byte[HEADER_SIZE];
        header[0] = (byte) 0xFF; // 魔数
        header[1] = (byte) 0xFE;
        header[2] = (byte) 0xFD;
        header[3] = (byte) 0xFC;
        // 存储消息长度
        byte[] lengthBytes = intToBytes(messageLength);
        System.arraycopy(lengthBytes, 0, header, 4, 4);
        // 计算校验和
        byte checksum = calculateChecksum(Arrays.copyOfRange(header, 0, 8));
        header[8] = checksum;
        return header;
    }

    /**
     * 检查文件容量是否足够
     */
    private static boolean checkCapacity(byte[] binary, byte[] dataToHide) {
        // 计算可用容量（使用数据区域）
        int availableBits = findGlyphDataArea(binary) * 8;
        int requiredBits = dataToHide.length * 8;
        return availableBits >= requiredBits;
    }

    /**
     * 查找数据区域
     */
    private static int findGlyphDataArea(byte[] binary) {
        // 简化处理：跳过字体头部，使用后半部分数据
        int startOffset = Math.min(1000, binary.length / 4);
        return Math.min(binary.length - startOffset, binary.length / 2);
    }

    /**
     * 嵌入数据到文件
     */
    private static byte[] embedData(byte[] binary, byte[] dataToHide) {
        byte[] result = binary.clone();
        // 确定嵌入起始位置
        int startOffset = Math.min(1000, result.length / 4);
        int availableLength = Math.min(result.length - startOffset, result.length / 2);
        // 将数据转换为位序列
        boolean[] bits = bytesToBits(dataToHide);
        // 使用LSB隐写技术嵌入数据
        for (int i = 0; i < bits.length && startOffset + i < result.length; i++) {
            // 只在字形数据区域进行隐写
            if (startOffset + i < startOffset + availableLength) {
                byte originalByte = result[startOffset + i];
                byte modifiedByte = (byte) ((originalByte & 0xFE) | (bits[i] ? 1 : 0));
                result[startOffset + i] = modifiedByte;
            }
        }
        return result;
    }

    /**
     * 将字节数组转换为位数组
     */
    private static boolean[] bytesToBits(byte[] bytes) {
        boolean[] bits = new boolean[bytes.length * 8];
        for (int i = 0; i < bytes.length; i++) {
            for (int j = 0; j < 8; j++) {
                bits[i * 8 + j] = ((bytes[i] >> (7 - j)) & 1) == 1;
            }
        }
        return bits;
    }

    /**
     * 合并两个字节数组
     */
    private static byte[] combineArrays(byte[] array1, byte[] array2) {
        byte[] result = new byte[array1.length + array2.length];
        System.arraycopy(array1, 0, result, 0, array1.length);
        System.arraycopy(array2, 0, result, array1.length, array2.length);
        return result;
    }

    /**
     * 整数转字节数组
     */
    private static byte[] intToBytes(int value) {
        return new byte[]{
                (byte) (value >> 24),
                (byte) (value >> 16),
                (byte) (value >> 8),
                (byte) value
        };
    }

    /**
     * 计算校验和
     */
    private static byte calculateChecksum(byte[] data) {
        byte checksum = 0;
        for (byte b : data) {
            checksum ^= b;
        }
        return checksum;
    }
}
